<script setup lang="ts">
import { computed, onMounted, PropType, ref, toRefs, watch } from 'vue'
import { ElMessage } from 'element-plus'
import { Plus, Delete } from '@element-plus/icons-vue'
import { useI18n } from 'vue-i18n'
import _ from 'lodash'

const props = defineProps({
  modelValue: { type: Boolean, required: true },
  name: { type: String, required: true },
  beanId: { type: [Number, String], default: null },
  values: { type: Object, required: true },
  initValues: { type: Function as PropType<(bean?: any) => any>, required: true },
  toValues: { type: Function as PropType<(bean: any) => any>, required: true },
  queryFunc: { type: Function as PropType<(id: any) => Promise<any>>, required: true },
  createFunc: { type: Function as PropType<(bean: any) => Promise<any>>, required: true },
  updateFunc: { type: Function as PropType<(bean: any) => Promise<any>>, required: true },
  deleteFunc: { type: Function as PropType<(id: any) => Promise<any>>, required: true },
  disableDelete: { type: Function as PropType<(bean: any) => boolean>, default: null },
  disableEdit: { type: Function as PropType<(bean: any) => boolean>, default: null },
  addable: { type: Boolean, default: true },
  action: { type: String as PropType<'add' | 'copy' | 'edit' | 'review'>, default: 'edit' },
  showId: { type: Boolean, default: true },
  perms: { type: String, default: null },
  focus: { type: Object, default: null },
  large: { type: Boolean, default: false },
  labelPosition: { type: String as PropType<'top' | 'right' | 'left'>, default: 'right' },
  labelWidth: { type: String, default: '150px' }
})
const emit = defineEmits({
  'update:modelValue': null,
  'update:values': null,
  finished: null,
  beanChange: null,
  beforeSubmit: null
})

const { name, beanId, focus, values, action, modelValue: visible } = toRefs(props)
const { t } = useI18n()
const loading = ref<boolean>(false)
const buttonLoading = ref<boolean>(false)
const form = ref<any>()
const bean = ref<any>(props.initValues())
const origValues = ref<any>()
const id = ref<any>()
const isEdit = computed(() => id.value != null && action.value === 'edit')

const review = computed(() => {
  const isReview = id.value != null && action.value === 'review'
  return isReview
})
const unsaved = computed(() => {
  return !loading.value && !_.isEqual(origValues.value, values.value)
})
const disabled = computed(() => props.disableEdit?.(bean.value) ?? false)
const title = computed(
  () =>
    `${name.value} - ${
      isEdit.value
        ? `${t(disabled.value ? 'detail' : 'edit')} (ID: ${id.value})`
        : `${t('action.add')}`
    }`
)
const loadBean = async () => {
  loading.value = true
  try {
    bean.value = id.value != null ? await props.queryFunc(id.value) : props.initValues(values.value)
    origValues.value = id.value != null ? props.toValues(bean.value) : bean.value

    emit('update:values', _.cloneDeep(origValues.value))
    emit('beanChange', bean.value)
    form.value?.resetFields()
  } finally {
    loading.value = false
  }
}
onMounted(() => {
  emit('update:values', props.initValues())
})
watch(visible, () => {
  if (visible.value) {
    //添加先给设置回默认空
    if(form.value){
      form.value.resetFields()
    }
    //编辑页面不需要重置，新增需要
    if(!beanId.value){
      emit('update:values', props.initValues(values.value))
    }
    if (id.value !== beanId.value) {
      id.value = beanId.value
    }
    if(id.value){
      loadBean()
    }
  }
})
// watch(id, () => {
//   loadBean()
// })
const handleAdd = () => {
  focus.value?.focus?.()
  id.value = undefined
}
const handleCancel = () => {
  emit('update:modelValue', false)
}
const handleSubmit = () => {
  form.value.validate(async (valid: boolean) => {
    if (!valid) return
    buttonLoading.value = true
    try {
      emit('beforeSubmit', values.value)
      if (isEdit.value) {
        await props.updateFunc(values.value)
      } else {
        await props.createFunc(values.value)
        focus.value?.focus?.()
        emit('update:values', props.initValues(values.value))
        form.value.resetFields()
      }
      ElMessage.success(t('common.success'))
      emit('update:modelValue', true)
      emit('finished', bean.value)
    } finally {
      buttonLoading.value = false
    }
  })
}
const handleDelete = async () => {
  buttonLoading.value = true
  try {
    await props.deleteFunc([id.value])
    emit('update:modelValue', false)
    ElMessage.success(t('common.success'))
    emit('finished')
  } finally {
    buttonLoading.value = false
  }
}
const submit = (
  executor: (
    values: any,
    payload: {
      isEdit: boolean
      form: any
      props: any
      focus: any
      loadBean: () => Promise<any>
      emit: any
    }
  ) => Promise<any>
) => {
  form.value.validate(async (valid: boolean) => {
    if (!valid) return
    buttonLoading.value = true
    try {
      emit('beforeSubmit', values.value)
      await executor(values.value, {
        isEdit: isEdit.value,
        form: form.value,
        props,
        focus: focus.value,
        loadBean,
        emit
      })

      emit('update:modelValue', true)
      emit('finished', bean.value)
    } finally {
      buttonLoading.value = false
    }
  })
}
const remove = async (
  executor: (
    values: any,
    payload: {
      isEdit: boolean
      form: any
      props: any
      focus: any
      loadBean: () => Promise<any>
      emit: any
    }
  ) => Promise<any>
) => {
  buttonLoading.value = true
  try {
    await executor(values.value, {
      isEdit: isEdit.value,
      form: form.value,
      props,
      focus: focus.value,
      loadBean,
      emit
    })
    emit('update:modelValue', false)
    ElMessage.success(t('common.success'))
    emit('finished')
  } finally {
    buttonLoading.value = false
  }
}
defineExpose({ form, submit, remove })
</script>

<template>
  <el-dialog
    :title="title"
    :close-on-click-modal="!unsaved"
    :model-value="modelValue"
    :width="large ? '98%' : '768px'"
    :top="large ? '16px' : '8vh'"
    @update:model-value="(event) => $emit('update:modelValue', event)"
    @opened="() => !isEdit && focus?.focus()"
  >
    <template #header>
      {{ name }} -
      <span v-if="isEdit">
        {{ $t(disabled ? 'action.detail' : 'action.edit') }}
        <span v-if="showId">(ID: {{ id }})</span>
      </span>
      <span v-else>{{ $t('action.add') }}</span>
    </template>
    <div v-loading="loading || buttonLoading" class="space-x-2">
      <slot
        name="header-action"
        :bean="bean"
        :is-edit="isEdit"
        :review="review"
        :disabled="disabled"
        :unsaved="unsaved"
        :disable-delete="disableDelete"
        :handle-delete="handleDelete"
      >
        <el-popconfirm
          v-if="isEdit"
          :title="$t('common.delMessage')"
          @confirm="() => handleDelete()"
        >
          <template #reference>
            <el-button v-hasPermi="[`cms:${perms}:delete`]" :icon="Delete">{{
              $t('action.delete')
            }}</el-button>
          </template>
        </el-popconfirm>
      </slot>
      <el-button type="primary" @click="handleCancel">{{ $t('common.back') }}</el-button>

      <slot
        name="header-status"
        :bean="bean"
        :is-edit="isEdit"
        :review="review"
        :disabled="disabled"
      ></slot>
    </div>
    <el-form
      ref="form"
      :class="['mt-5', 'pr-5']"
      :model="values"
      :disabled="disabled"
      :label-width="labelWidth"
      :label-position="labelPosition"
      scroll-to-error
    >
      <slot :bean="bean" :is-edit="isEdit" :review="review" :disabled="disabled"></slot>
      <div v-if="!disabled" v-loading="buttonLoading">
        <slot
          name="footer-action"
          :bean="bean"
          :is-edit="isEdit"
          :review="review"
          :disabled="disabled"
          :handle-submit="handleSubmit"
        >
          <el-button
            v-hasPermi="[`cms:${perms}:update`]"
            type="primary"
            native-type="submit"
            @click.prevent="() => handleSubmit()"
          >
            {{ $t('common.save') }}
          </el-button>
        </slot>
      </div>
    </el-form>
  </el-dialog>
</template>
